#![allow(dead_code)]
struct OptionLrn {}

impl OptionLrn {
    pub fn hello() {
        println!("Option learning!");
    }
}

#[cfg(test)]
mod tests {
    // use super::*;
    use std::error::Error;

    #[test]
    fn divide_test() {
        fn divide(a: f64, b: f64) -> Result<f64, String> {
            // NOTE: rust 测试为零的方法 EPSLION 为最小的浮点数
            if b.abs() < f64::EPSILON {
                Err("cannot divide by 0".into())
            } else {
                Ok(a / b)
            }
        }

        assert_eq!(divide(10f64, 2f64), Ok(5f64));
        assert_eq!(divide(5f64, 0f64), Err("cannot divide by 0".into()));
    }

    #[test]
    /// Rust 的指针类型必须始终指向有效位置。没有 “null” 引用。相反，Rust 有 optional 指针，就像可选的拥有所有权的 box，Option<Box<T>>。
    fn test_check_null_ptr() {
        let optional = None;
        check_optional(optional);

        let optional = Some(Box::new(7));
        check_optional(optional);

        fn check_optional(optional: Option<Box<i32>>) {
            match optional {
                None => {
                    println!("The optional is None");
                }
                Some(x) => {
                    println!("The optional is Some with value: {}", x);
                }
            }
        }
    }

    #[test]
    /// Option::ok_or() 把 Option<T> 转换成 Result<T, E>
    fn test_ok_or() {
        let mut stack = vec![1, 2, 3, 4];
        let result = stack.pop().ok_or("Stack too small");
        println!("result: {:?}", result);
    }

    #[test]
    fn test_question_mark() {
        let mut stack = vec![1, 2, 3, 4];
        let result = add_last_two_numbers(&mut stack);
        println!("result: {:?}", result.unwrap());
        // return Some(result);
        fn add_last_two_numbers(stack: &mut Vec<i32>) -> Option<i32> {
            Some(stack.pop()? + stack.pop()?)
        }
    }

    #[test]
    /// NOTE: map() 会消耗 self
    /// Option, Result, iter有map()方法
    fn test_map() {
        let maybe_some_string = Some(String::from("Hello, World!"));
        // `Option::map` 按值获取 self，消耗 `maybe_some_string`
        let maybe_some_len = maybe_some_string.map(|s| s.len());
        assert_eq!(maybe_some_len, Some(13));
        println!("{:?}", maybe_some_len);

        let x: Option<&str> = None;
        assert_eq!(x.map(|s| s.len()), None);

        let maybe_some_string = Some(String::from("Hello, World!"));
        // `Option::map` 按值获取 self，消耗 `maybe_some_string`
        let maybe_some_len = maybe_some_string.map(|mut s| s.pop().unwrap());
        assert_eq!(maybe_some_len, Some('!'));
        println!("{:?}", maybe_some_len);
    }

    #[test]
    // NOTE: 处理Some(), Ok()的值
    // Err 返回值： 1. Box<dyn Error> 2. &'static str
    fn math_test() -> Result<(), &'static str> {
        let x = Some(1).map(|x| x + 1);
        assert_eq!(x, Some(2));

        // Result 须有error 的返回值, 因此需要增加注释
        let y = Ok::<i32, &str>(1).map(|x| x + 1)?;
        assert_eq!(y, 2);

        // Ok() 没有实现 PartialEq
        let z: Result<i32, &str> = Ok(3).map(|x| x * 2);
        assert_eq!(z.unwrap(), 6);

        Ok(())
    }

    #[test]
    // NOTE: err type: std::num::ParseIntError, 可以用Box<dyn std::error::Error>替代
    fn error_test() -> Result<(), Box<dyn Error>> {
        let err = "NaN".parse::<u32>().unwrap_err();
        println!("error: {}", err);
        // assert_eq!(err, std::num::ParseIntError { kind: val });
        Ok(())
    }
}
